package cn.com.twoke.app.pin_dou_dou.core;


import cn.com.twoke.app.pin_dou_dou.utils.ColorUtils;
import cn.com.twoke.app.pin_dou_dou.utils.GraphicsUtils;
import cn.com.twoke.app.pin_dou_dou.utils.ImageLossLessUtils;
import cn.com.twoke.app.pin_dou_dou.utils.ImageProcessingUtils;
import cn.com.twoke.app.pin_dou_dou.utils.PdfInstructionUtils;
import cn.hutool.core.convert.CastUtil;
import cn.hutool.core.img.ImgUtil;
import cn.hutool.core.lang.Version;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONTokener;
import lombok.extern.slf4j.Slf4j;

import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.text.NumberFormatter;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.InputStream;
import java.net.URL;
import java.text.NumberFormat;
import java.util.*;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.IntStream;


@Slf4j
public class PinDouDouPanel extends JFrame {
    private static final int DEFAULT_DIALOG_WIDTH = 1000;
    private static final int DEFAULT_DIALOG_HEIGHT = 800;
    private static final int DEFAULT_BLOCK_SIZE = 32;
    private static final int COLOR_INFO_BLOCK_WIDTH = 100;
    private static final int COLOR_INFO_BLOCK_HEIGHT = 40;
    private static final int COLOR_INFO_MARGIN_TOP = 10;
    private static final int COLOR_INFO_MARGIN_BOTTOM = 20;
    private static final Font DEFAULT_FONT = new Font(Font.SANS_SERIF, Font.BOLD, 12);
    static Collection<Color> colorList;
    static JSONObject colorMap;
    // 颜色方案
    private static String colorScheme = Settings.getColorScheme();

    static {
        try (InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("color-map.json")) {
            if (is == null) {
                log.error("无法找到 color-map.json 资源文件");
                throw new RuntimeException("无法找到 color-map.json 资源文件");
            }
            colorMap = new JSONObject(new JSONTokener(is, null));
            // 初始化时加载默认颜色方案
        } catch (Exception e) {
            log.error("标准颜色数据加载失败！", e);
        }
    }

    static {
        updateColorList();
    }

    // 当前显示的图片类型 (0: 原图, 1: 拼豆图)
    private int imageType = 0;
    // 缓存的拼豆图
    private BufferedImage cachedPinDouImage = null;
    // 缓存的无分区拼豆图
    private BufferedImage cachedOriginalPinDouImage = null;
    // 分区图像列表
    private List<BufferedImage> partitionImages = new ArrayList<>();
    // 分区名称列表
    private List<String> partitionNames = new ArrayList<>();
    // 分区颜色统计信息
    private List<Map<String, Integer>> partitionColorStats = new ArrayList<>();
    private BufferedImage bufferedImage;
    private double scale;
    private File selectedFile;
    private JFormattedTextField widthTF;
    private JFormattedTextField heightTF;
    private JProgressBar progressBar;
    private JLabel showImage;
    // 用于显示默认信息的标签
    private JLabel infoLabel;
    // 添加对CardLayout的引用，方便切换视图
    private CardLayout imagePanelLayout;
    private JPanel imagePanel;
    // 显示当前颜色方案的标签
    private JLabel colorSchemeLabel;

    public PinDouDouPanel(String title, String icon, Integer width, Integer height) {
        this.setTitle(title);
        URL resource = Thread.currentThread().getContextClassLoader().getResource(icon);
        if (resource != null) {
            this.setIconImage(new ImageIcon(resource).getImage());
        }
        this.init();
        this.setResizable(true); // 允许调整窗口大小
        this.setSize(800, 600); // 设置初始窗口大小
        this.setMinimumSize(new Dimension(600, 400)); // 设置最小窗口大小
        this.setLocationRelativeTo(null);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // 添加窗口大小改变监听器
        this.addComponentListener(new ComponentAdapter() {
            @Override
            public void componentResized(ComponentEvent e) {
                // 窗口大小改变时，重新调整图片大小
                resizeImage();
            }
        });

        // 在一个后台线程中检查更新
        checkForUpdates();

        this.setVisible(true);
    }

    /**
     * 根据当前颜色方案更新颜色列表
     */
    private static void updateColorList() {
        if (colorMap != null) {
            colorList = new ArrayList<>();
            for (String colorKey : colorMap.keySet()) {
                JSONObject colorInfo = colorMap.getJSONObject(colorKey);
                if (colorInfo != null && colorInfo.containsKey(colorScheme)) {
                    colorList.add(new Color(NumberUtil.parseInt(colorKey.replace("#", "0x"))));
                }
            }
        }
    }

    /**
     * 检查更新
     */
    private void checkForUpdates() {
        checkForUpdates(false);
    }

    /**
     * 检查更新
     *
     * @param manual 是否是用户手动检查
     */
    private void checkForUpdates(boolean manual) {
        new Thread(() -> {
            try {
                // 获取当前版本 (这里写死为1.0.0，实际应用中应该从配置文件或MANIFEST.MF中读取)
                Version currentVersion = new Version(Settings.VERSION);

                // 创建Gitee更新检测器
                GiteeUpdateChecker updateChecker = new GiteeUpdateChecker("twoke", // Gitee用户名
                        "pin-dou-dou", // Gitee仓库名
                        "拼豆图设计器" // 应用程序名称
                );

                // 检查更新
                UpdateInfo updateInfo = updateChecker.checkForUpdate(currentVersion);

                if (updateInfo != null) {
                    // 有新版本
                    SwingUtilities.invokeLater(() -> {
                        UpdateDialog.showUpdateDialog(this, updateChecker.getAppName(), currentVersion, updateInfo, new UpdateDownloader.DownloadCallback() {
                            @Override
                            public void onComplete(boolean success, File file, String errorMsg) {
                                if (success) {
                                    SwingUtilities.invokeLater(() -> {
                                        JOptionPane.showMessageDialog(PinDouDouPanel.this, "更新包已下载到: " + file.getAbsolutePath(), "下载完成", JOptionPane.INFORMATION_MESSAGE);
                                    });
                                } else {
                                    SwingUtilities.invokeLater(() -> {
                                        JOptionPane.showMessageDialog(PinDouDouPanel.this, "下载失败: " + errorMsg, "下载失败", JOptionPane.ERROR_MESSAGE);
                                    });
                                }
                            }
                        });
                    });
                } else if (manual) {
                    // 用户手动检查更新，但没有新版本
                    SwingUtilities.invokeLater(() -> {
                        JOptionPane.showMessageDialog(this, "当前版本已是最新版本。", "检查更新", JOptionPane.INFORMATION_MESSAGE);
                    });
                }
            } catch (Exception e) {
                log.error("检查更新时发生错误", e);
                if (manual) {
                    SwingUtilities.invokeLater(() -> {
                        JOptionPane.showMessageDialog(this, "检查更新时发生错误: " + e.getMessage(), "检查更新", JOptionPane.ERROR_MESSAGE);
                    });
                }
            }
        }).start();
    }

    /**
     * 根据窗口大小重新调整图片
     */
    private void resizeImage() {
        if (imageType == 0 && bufferedImage != null) {
            // 调整原图大小
            displayOriginalImage();
        } else if (imageType == 1 && cachedPinDouImage != null) {
            // 调整拼豆图大小
            displayPinDouImage(cachedPinDouImage);
        }
    }

    private void init() {
        this.setLayout(new BorderLayout(5, 5));

        // 创建顶部控制面板
        Component topPanel = createTopPanel();
        this.add(topPanel, BorderLayout.NORTH);

        // 创建中部显示面板
        Component centerPanel = createCenterPanel();
        this.add(centerPanel, BorderLayout.CENTER);

        // 创建底部日志面板
        Component bottomPanel = createBottomPanel();
        this.add(bottomPanel, BorderLayout.SOUTH);
    }

    private Component createBottomPanel() {
        // 创建日志文本区域
        JTextArea logTextArea = new JTextArea();
        logTextArea.setEditable(false);
        logTextArea.setFont(new Font("黑体", Font.PLAIN, 12));
        logTextArea.setRows(10); // 设置日志区域的行数

        // 日志显示区域
        JScrollPane scrollPane = new JScrollPane(logTextArea);
        scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
        scrollPane.setPreferredSize(new Dimension(800, 150)); // 设置日志区域的首选大小
        JTextAreaAppender.setTextArea(logTextArea);

        // 创建进度条
        progressBar = new JProgressBar();
        progressBar.setValue(0);
        progressBar.setStringPainted(true);
        progressBar.setVisible(false); // 初始时不可见

        // 创建显示当前颜色方案的标签
        colorSchemeLabel = new JLabel("当前颜色方案: " + colorScheme);
        colorSchemeLabel.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 0)); // 添加一些左边距

        // 创建一个面板来包含日志区域和进度条
        JPanel panel = new JPanel();
        panel.setLayout(new BorderLayout());
        panel.add(scrollPane, BorderLayout.CENTER);

        // 创建一个底部面板来放置进度条和颜色方案标签
        JPanel bottomPanel = new JPanel(new BorderLayout());
        bottomPanel.add(progressBar, BorderLayout.CENTER);
        bottomPanel.add(colorSchemeLabel, BorderLayout.WEST);
        panel.add(bottomPanel, BorderLayout.SOUTH);

        return panel;
    }

    private Component createTopPanel() {
        JPanel panel = new JPanel();
        panel.setLayout(new FlowLayout(FlowLayout.LEFT));

        // 选择图片按钮
        JButton selectImageButton = new JButton("选择图片");
        selectImageButton.addActionListener(this::onClickSelectImage);
        panel.add(selectImageButton);

        // 宽度输入框
        NumberFormatter numberFormatter = createNumberFormatter();
        widthTF = new JFormattedTextField(numberFormatter);
        widthTF.setValue(100); // 默认宽度
        widthTF.setColumns(5);
        widthTF.addPropertyChangeListener("value", e -> {
            if (Settings.isLockAspectRatio()) {
                Object value = widthTF.getValue();
                if (value instanceof Number && bufferedImage != null) {
                    int newWidth = ((Number) value).intValue();
                    heightTF.setValue((int) (newWidth * scale));
                }
            }
        });
        panel.add(new JLabel("宽度（颗）"));
        panel.add(widthTF);

        // 高度输入框
        heightTF = new JFormattedTextField(numberFormatter);
        heightTF.setValue(100); // 默认高度
        heightTF.setColumns(5);
        heightTF.setEditable(!Settings.isLockAspectRatio()); // 根据设置决定是否可编辑
        panel.add(new JLabel("高度（颗）"));
        panel.add(heightTF);

        // 生成拼豆图按钮
        JButton generateButton = new JButton("生成拼豆图");
        generateButton.addActionListener(e -> {
            if (bufferedImage == null) {
                JOptionPane.showMessageDialog(this, "请先选择一张图片", "提示", JOptionPane.INFORMATION_MESSAGE);
                return;
            }
            new Thread(() -> {
                try {
                    this.onClickGenerateImage(e);
                } catch (Exception ex) {
                    log.error("生成拼豆图时发生错误", ex);
                    SwingUtilities.invokeLater(() -> {
                        JOptionPane.showMessageDialog(this, "生成拼豆图时发生错误: " + ex.getMessage(), "错误", JOptionPane.ERROR_MESSAGE);
                    });
                }
            }).start();
        });
        panel.add(generateButton);

        // 生成PDF说明书按钮
        JButton generatePdfButton = new JButton("生成PDF说明书");
        generatePdfButton.addActionListener(e -> {
            if (cachedPinDouImage == null) {
                JOptionPane.showMessageDialog(this, "请先生成拼豆图", "提示", JOptionPane.INFORMATION_MESSAGE);
                return;
            }
            new Thread(() -> {
                try {
                    this.generatePdfInstruction();
                } catch (Exception ex) {
                    log.error("生成PDF说明书时发生错误", ex);
                    SwingUtilities.invokeLater(() -> {
                        JOptionPane.showMessageDialog(this, "生成PDF说明书时发生错误: " + ex.getMessage(), "错误", JOptionPane.ERROR_MESSAGE);
                    });
                }
            }).start();
        });
        panel.add(generatePdfButton);

        // 设置按钮
        JButton settingsButton = new JButton("设置");
        settingsButton.addActionListener(e -> {
            showSettingsDialog();
        });
        panel.add(settingsButton);

        // 检查更新按钮
        JButton checkUpdateButton = new JButton("检查更新");
        checkUpdateButton.addActionListener(e -> {
            checkForUpdates(true);
        });
        panel.add(checkUpdateButton);

        return panel;
    }

    private Component createCenterPanel() {
        // 创建用于显示生成后图片的标签
        showImage = new JLabel();
        showImage.setVerticalAlignment(SwingConstants.CENTER);
        showImage.setHorizontalAlignment(SwingConstants.CENTER);

        // 创建用于显示默认信息的标签
        infoLabel = new JLabel("请选择一张图片开始生成拼豆图", SwingConstants.CENTER);
        infoLabel.setFont(new Font("宋体", Font.PLAIN, 18));

        // 创建一个卡片布局的面板，用于在默认信息和生成的图片之间切换
        imagePanel = new JPanel(new CardLayout());
        imagePanelLayout = (CardLayout) imagePanel.getLayout();
        imagePanel.add(infoLabel, "info");
        imagePanel.add(showImage, "image");

        // 默认显示信息标签
        imagePanelLayout.show(imagePanel, "info");

        return imagePanel;
    }

    /**
     * 选择图片
     *
     * @param actionEvent
     */
    private void onClickSelectImage(ActionEvent actionEvent) {
        // 创建文件选择器
        JFileChooser fileChooser = new JFileChooser();

        // 设置对话框标题
        fileChooser.setDialogTitle("选择图片文件");

        // 设置初始目录（可选）
        fileChooser.setCurrentDirectory(new File(System.getProperty("user.dir")));

        // 设置文件选择模式：只能选择文件
        fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);

        // 设置多选：不允许选择多个文件
        fileChooser.setMultiSelectionEnabled(false);

        // 创建图片文件过滤器
        FileNameExtensionFilter imageFilter = new FileNameExtensionFilter("图片文件 (*.jpg, *.jpeg, *.png, *.gif, *.bmp)", "jpg", "jpeg", "png", "gif", "bmp");

        // 设置文件过滤器
        fileChooser.setFileFilter(imageFilter);

        // 添加所有文件选项（让用户仍然可以选择其他文件类型）
        fileChooser.setAcceptAllFileFilterUsed(true);

        // 显示打开对话框
        int result = fileChooser.showOpenDialog(this);

        // 处理用户选择
        if (result == JFileChooser.APPROVE_OPTION) {
            selectedFile = fileChooser.getSelectedFile();
            log.info("选择图片 {}", selectedFile.getAbsolutePath());

            // 尝试加载和显示图片
            try {
                bufferedImage = ImageProcessingUtils.loadImage(selectedFile.getAbsolutePath());
                // 缩放图片以适应标签（保持宽高比）
                scale = (double) bufferedImage.getHeight() / (double) bufferedImage.getWidth();
                int width = 100;
                int height = (int) (width * scale);
                log.info("图片比例 高/宽 = {}", scale);
                log.info("拼豆图默认宽度 {}", width);
                log.info("拼豆图默认高度 {}", height);

                // 更新宽度和高度输入框的值
                widthTF.setValue(width);
                heightTF.setValue(height);

                // 显示原图
                displayOriginalImage();
            } catch (Exception ex) {
                log.error("无法加载选择的图片文件！", ex);
                JOptionPane.showMessageDialog(this, "无法加载选择的图片文件！", "错误", JOptionPane.ERROR_MESSAGE);
            }
        } else {
            log.info("取消选择");
        }

    }

    private NumberFormatter createNumberFormatter() {
        // 创建数字格式器
        NumberFormat format = NumberFormat.getNumberInstance();
        NumberFormatter formatter = new NumberFormatter(format);

        // 设置格式化器属性
        formatter.setValueClass(Integer.class); // 或者 Double.class
        formatter.setMinimum(0); // 最小值
        formatter.setMaximum(100000); // 最大值
        formatter.setAllowsInvalid(false); // 不允许无效输入
//        formatter.setCommitsOnValidEdit(true); // 有效编辑时立即提交

        return formatter;
    }

    /**
     * 显示设置对话框
     */
    private void showSettingsDialog() {
        // 保存当前主题
        String currentTheme = Settings.getTheme();

        SettingsDialog dialog = new SettingsDialog(this);
        dialog.setVisible(true);

        // 对话框关闭后，重新加载设置
        String newWorkingDir = Settings.getWorkingDir();
        String newColorScheme = Settings.getColorScheme();
        String newTheme = Settings.getTheme();

        // 如果颜色方案发生了变化，则更新颜色列表和界面显示
        if (!newColorScheme.equals(colorScheme)) {
            colorScheme = newColorScheme;
            updateColorList();
            // 更新界面显示
            if (colorSchemeLabel != null) {
                colorSchemeLabel.setText("当前颜色方案: " + colorScheme);
            }
        }

        // 更新高度输入框的可编辑性
        if (heightTF != null) {
            heightTF.setEditable(!Settings.isLockAspectRatio());
        }

        // 如果主题发生了变化，则更新主题
        if (!newTheme.equals(currentTheme)) {
            try {
                if ("FlatDarkLaf".equals(newTheme)) {
                    com.formdev.flatlaf.FlatDarkLaf.setup();
                } else {
                    com.formdev.flatlaf.FlatLightLaf.setup();
                }
                // 更新所有窗口的外观
                SwingUtilities.updateComponentTreeUI(this);
                // 如果有其他窗口，也需要更新它们的外观
                // SwingUtilities.updateComponentTreeUI(otherWindow);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 显示原始图片
     */
    private void displayOriginalImage() {
        if (bufferedImage != null) {
            imageType = 0; // 设置当前显示的图片类型为原图
            // 在UI线程中更新显示的图像
            SwingUtilities.invokeLater(() -> {
                scaleAndDisplayImage(bufferedImage);
            });
        }
    }

    /**
     * 显示拼豆图片
     *
     * @param image 要显示的拼豆图片
     */
    private void displayPinDouImage(BufferedImage image) {
        if (image != null) {
            imageType = 1; // 设置当前显示的图片类型为拼豆图
            Integer width = (Integer) widthTF.getValue();
            Integer height = (Integer) heightTF.getValue();
            cachedPinDouImage = image.getSubimage(0, 0, width * DEFAULT_BLOCK_SIZE, height * DEFAULT_BLOCK_SIZE);
            // 在UI线程中更新显示的图像
            SwingUtilities.invokeLater(() -> {
                scaleAndDisplayImage(image);
            });
        }
    }

    /**
     * 缩放并显示图片
     *
     * @param image 要缩放和显示的图片
     */
    private void scaleAndDisplayImage(BufferedImage image) {
        // 计算按比例缩放后的图片尺寸
        Dimension panelSize = imagePanel.getSize();
        if (panelSize.width <= 0 || panelSize.height <= 0) {
            // 如果面板尺寸还没有确定，则使用默认尺寸
            panelSize = new Dimension(500, 500);
        }
        int panelWidth = Math.max(panelSize.width - 20, 100); // 减去一些边距
        int panelHeight = Math.max(panelSize.height - 20, 100);

        // 获取原始图片的宽高
        int imgWidth = image.getWidth();
        int imgHeight = image.getHeight();

        // 计算缩放比例，保持图片的宽高比
        double scale = Math.min((double) panelWidth / imgWidth, (double) panelHeight / imgHeight);

        // 计算缩放后的图片尺寸
        int scaledWidth = (int) (imgWidth * scale);
        int scaledHeight = (int) (imgHeight * scale);

        // 缩放图片
        Image scaledResult = ImgUtil.scale(image, scaledWidth, scaledHeight);

        // 设置图片到 JLabel
        showImage.setIcon(new ImageIcon(scaledResult));
        showImage.revalidate();
        showImage.repaint();

        // 切换到图片显示视图
        imagePanelLayout.show(imagePanel, "image");
    }

    private void onClickGenerateImage(ActionEvent actionEvent) {
        // 获取用户输入的宽度和高度
        int width = (Integer) widthTF.getValue();
        int height = (Integer) heightTF.getValue();

        // 初始化进度条
        SwingUtilities.invokeLater(() -> {
            progressBar.setValue(0);
            progressBar.setMaximum(width * height);
            progressBar.setString("正在生成拼豆图...");
            progressBar.setVisible(true); // 显示进度条
        });

        try {
            long startTime = System.currentTimeMillis(); // 记录开始时间

            // 缩放原始图片以匹配用户指定的尺寸
            BufferedImage scaledImage = ImageProcessingUtils.scaleImage(bufferedImage, width, height, BufferedImage.TYPE_INT_RGB);

            // 创建用于绘制无分区拼豆图的缓冲图像
            BufferedImage originalResult = ImageProcessingUtils.createImage(width * DEFAULT_BLOCK_SIZE, height * DEFAULT_BLOCK_SIZE, BufferedImage.TYPE_INT_RGB);
            Graphics2D originalG2d = originalResult.createGraphics();

            // 启用抗锯齿以获得更清晰的文本
            GraphicsUtils.enableAntialiasing(originalG2d);

            // 用于统计颜色使用数量的Map
            // 使用 ConcurrentHashMap 以提高在多线程环境下的性能
            Map<String, Integer> colorCount = new ConcurrentHashMap<>();

            // 遍历每个像素点，绘制对应的拼豆
            // 使用多线程并行处理图像行，以提高性能
            final BufferedImage finalScaledImage = scaledImage; // 需要为内部类创建final副本
            final int[] completedRows = new int[1]; // 用于跟踪完成的行数
            final Object lock = new Object(); // 用于同步访问completedRows和更新进度条

            // 并行流处理每一行
            IntStream.range(0, width).parallel().forEach(x -> {
                for (int y = 0; y < height; y++) {
                    // 获取缩放后图片在(x, y)位置的颜色
                    int rgb = finalScaledImage.getRGB(x, y); // 直接获取rgb值，避免创建Color对象
                    Color pixelColor = new Color(rgb);

                    // 确保colorList不为空
                    if (colorList == null || colorList.isEmpty()) {
                        log.error("颜色列表为空或未初始化");
                        continue; // 跳过当前像素的处理
                    }

                    // 找到最接近的标准拼豆颜色
                    Color closestColor = ColorUtils.findClosestColor(pixelColor, colorList);

                    // 绘制拼豆方块
                    synchronized (originalG2d) { // Graphics2D 不是线程安全的，需要同步
                        originalG2d.setColor(closestColor);
                        originalG2d.fillRect(x * DEFAULT_BLOCK_SIZE, y * DEFAULT_BLOCK_SIZE, DEFAULT_BLOCK_SIZE, DEFAULT_BLOCK_SIZE);

                        // 绘制颜色标识字符
                        String colorKey = "#" + Integer.toHexString(closestColor.getRGB() & 0x00ffffff).toUpperCase();
                        JSONObject colorInfo = colorMap.getJSONObject(colorKey);

                        if (Objects.nonNull(colorInfo)) {
                            // 获取颜色标识字符
                            String mark = colorInfo.getStr(colorScheme); // 使用当前颜色方案
                            if (mark != null) {
                                String mapKey = colorKey + "_" + mark;

                                // 更新颜色计数 (HashMap不是线程安全的，需要同步)
                                synchronized (colorCount) {
                                    colorCount.put(mapKey, colorCount.getOrDefault(mapKey, 0) + 1);
                                }

                                // 设置对比色用于绘制文本
                                originalG2d.setColor(ColorUtils.getContrastColor(closestColor));
                                originalG2d.setFont(DEFAULT_FONT);

                                // 获取字体度量以计算文本位置
                                FontMetrics metrics = originalG2d.getFontMetrics();

                                // 计算文本居中位置
                                int blockX = x * DEFAULT_BLOCK_SIZE;
                                int blockY = y * DEFAULT_BLOCK_SIZE;
                                GraphicsUtils.drawCenteredString(originalG2d, mark, blockX, blockY, DEFAULT_BLOCK_SIZE, DEFAULT_BLOCK_SIZE);
                            }
                        }
                    }
                }

                // 更新进度条
                // 为了减少对Swing线程的频繁调用，可以每处理完几行再更新一次
                // 这里简化为每行都更新，但在实际应用中可以考虑批量更新
                synchronized (lock) {
                    completedRows[0]++;
                    final int progress = completedRows[0] * height; // completedRows[0]是已完成的列数
                    if (progress % 100 == 0 || progress == width * height) { // 每100个像素或完成时更新
                        SwingUtilities.invokeLater(() -> {
                            progressBar.setValue(Math.min(progress, width * height)); // 确保不超过最大值
                            progressBar.setString(String.format("正在生成拼豆图... %.2f%%", (progress * 100.0) / (width * height)));
                        });
                    }
                }
            });

            // 保存无分区的拼豆图
            cachedOriginalPinDouImage = originalResult;

            // 获取分区设置
            int partitionWidth = Settings.getPartitionWidth();
            int partitionHeight = Settings.getPartitionHeight();

            // 创建用于绘制带分区线拼豆图的缓冲图像
            BufferedImage result = ImageProcessingUtils.createImage(width * DEFAULT_BLOCK_SIZE, height * DEFAULT_BLOCK_SIZE, BufferedImage.TYPE_INT_RGB);
            Graphics2D g2d = result.createGraphics();

            // 启用抗锯齿以获得更清晰的文本
            GraphicsUtils.enableAntialiasing(g2d);

            // 将无分区的拼豆图绘制到带分区线的图像上
            g2d.drawImage(originalResult, 0, 0, null);

            // 绘制分区线和标签
            drawPartitions(g2d, width, height, partitionWidth, partitionHeight);

            // 计算颜色信息区域的尺寸
            int cols = width * DEFAULT_BLOCK_SIZE / COLOR_INFO_BLOCK_WIDTH;
            // 确保 cols 至少为1，避免除以0
            cols = Math.max(1, cols);
            int rows = colorCount.size() % cols == 0 ? colorCount.size() / cols : colorCount.size() / cols + 1;
            // 确保 rows 至少为1
            rows = Math.max(1, rows);
            int colorInfoAreaHeight = COLOR_INFO_MARGIN_TOP + rows * COLOR_INFO_BLOCK_HEIGHT + COLOR_INFO_MARGIN_BOTTOM;

            // 创建包含拼豆图和颜色信息的总图像
            BufferedImage total = new BufferedImage(width * DEFAULT_BLOCK_SIZE, height * DEFAULT_BLOCK_SIZE + colorInfoAreaHeight, BufferedImage.TYPE_INT_RGB);
            Graphics2D totalG2d = total.createGraphics();

            // 启用抗锯齿
            GraphicsUtils.enableAntialiasing(totalG2d);

            // 填充背景为白色
            totalG2d.setColor(Color.WHITE);
            totalG2d.fillRect(0, 0, total.getWidth(), total.getHeight());

            // 绘制拼豆图
            totalG2d.drawImage(result, 0, 0, null);

            // 绘制颜色信息
            int index = 0;
            // 使用 synchronized block 来安全地遍历 ConcurrentHashMap
            synchronized (colorCount) {
                for (String colorKeyWithMard : colorCount.keySet()) {
                    int row = index / cols;
                    int col = index % cols;

                    // 解析颜色键和标识字符
                    List<String> parts = StrUtil.split(colorKeyWithMard, "_");
                    String colorKey = parts.get(0);
                    String mark = parts.get(1);

                    // 获取颜色对象
                    Color color = new Color(NumberUtil.parseInt(colorKey.replace("#", "0x")));

                    // 计算颜色信息块的位置
                    int blockX = col * COLOR_INFO_BLOCK_WIDTH;
                    int blockY = height * DEFAULT_BLOCK_SIZE + COLOR_INFO_MARGIN_TOP + row * COLOR_INFO_BLOCK_HEIGHT;

                    // 绘制颜色块和颜色标识字符
                    GraphicsUtils.drawBlockWithCenteredText(totalG2d, blockX, blockY, DEFAULT_BLOCK_SIZE, DEFAULT_BLOCK_SIZE, color, ColorUtils.getContrastColor(color), mark);

                    // 绘制颜色使用数量
                    totalG2d.setColor(Color.BLACK);
                    String countText = "x " + colorCount.get(colorKeyWithMard) + "颗";
                    // 重新计算 textY，因为 drawBlockWithCenteredText 不返回文本位置
                    FontMetrics metrics = totalG2d.getFontMetrics();
                    int textY = blockY + (DEFAULT_BLOCK_SIZE - metrics.getHeight()) / 2 + metrics.getAscent();
                    totalG2d.drawString(countText, blockX + DEFAULT_BLOCK_SIZE + 2, textY);

                    index++;
                }
            }

            // 生成分区图像和统计信息
            generatePartitionImagesAndStats(width, height, partitionWidth, partitionHeight, scaledImage);

            // 释放资源
            originalG2d.dispose();
            g2d.dispose();
            totalG2d.dispose();
            result = null;
            scaledImage = null;

            long endTime = System.currentTimeMillis();
            log.info("图片生成耗时: {} ms", (endTime - startTime));

            // 在UI线程中更新显示的图像
            SwingUtilities.invokeLater(() -> {
                // 缓存拼豆图
                displayPinDouImage(total);

                // 更新进度条完成状态
                progressBar.setValue(width * height);
                progressBar.setString("拼豆图生成完成");
                progressBar.setVisible(true); // 显示进度条
            });

            // 保存图像到文件
            String outputFile = Settings.getWorkingDir() + File.separator + "temp.jpeg";
            ImageLossLessUtils.saveLossLessImage(total, outputFile, "jpeg");
            log.info("拼豆图输出：{}", outputFile);
            log.info("拼豆总颗数：{}", colorCount.values().stream().mapToInt(item -> item).sum());
        } catch (Exception e) {
            log.error("生成拼豆图时发生错误", e);
            SwingUtilities.invokeLater(() -> {
                JOptionPane.showMessageDialog(this, "生成拼豆图时发生错误: " + e.getMessage(), "错误", JOptionPane.ERROR_MESSAGE);
                progressBar.setString("生成失败");
                progressBar.setVisible(true); // 显示进度条
            });
        }
    }

    /**
     * 绘制分区线和标签
     *
     * @param g2d             Graphics2D对象
     * @param width           拼豆图宽度（块数）
     * @param height          拼豆图高度（块数）
     * @param partitionWidth  分区宽度（块数）
     * @param partitionHeight 分区高度（块数）
     */
    private void drawPartitions(Graphics2D g2d, int width, int height, int partitionWidth, int partitionHeight) {
        // 设置明显的蓝色画笔
            Color partitionColor = Color.BLACK;
        g2d.setColor(partitionColor);
        g2d.setStroke(new BasicStroke(3)); // 设置线宽为3像素，更明显

        // 绘制垂直分区线
        for (int x = partitionWidth; x < width; x += partitionWidth) {
            int xPos = x * DEFAULT_BLOCK_SIZE;
            g2d.drawLine(xPos, 0, xPos, height * DEFAULT_BLOCK_SIZE);
        }

        // 绘制水平分区线
        for (int y = partitionHeight; y < height; y += partitionHeight) {
            int yPos = y * DEFAULT_BLOCK_SIZE;
            g2d.drawLine(0, yPos, width * DEFAULT_BLOCK_SIZE, yPos);
        }

        // 绘制分区标签
        // 字体大小设置为分区的50%
        int fontSize = (int) (Math.min(partitionWidth, partitionHeight) * DEFAULT_BLOCK_SIZE * 0.5);
        Font labelFont = new Font(Font.SANS_SERIF, Font.BOLD, fontSize);
        g2d.setFont(labelFont);
        g2d.setColor(new Color(255, 0, 0, 128)); // 红色，50%透明度

        // 计算分区数量
        int horizontalPartitions = (width + partitionWidth - 1) / partitionWidth;
        int verticalPartitions = (height + partitionHeight - 1) / partitionHeight;

        // 为每个分区绘制标签
        char labelChar = 'A';
        for (int py = 0; py < verticalPartitions; py++) {
            for (int px = 0; px < horizontalPartitions; px++) {
                // 计算分区的中心位置
                int partitionStartX = px * partitionWidth;
                int partitionStartY = py * partitionHeight;
                int partitionEndX = Math.min(partitionStartX + partitionWidth, width);
                int partitionEndY = Math.min(partitionStartY + partitionHeight, height);

                // 计算中心点
                int centerX = (partitionStartX + partitionEndX) / 2 * DEFAULT_BLOCK_SIZE;
                int centerY = (partitionStartY + partitionEndY) / 2 * DEFAULT_BLOCK_SIZE;

                // 绘制标签字符
                String label = String.valueOf(labelChar);
                FontMetrics metrics = g2d.getFontMetrics();
                int textWidth = metrics.stringWidth(label);
                int textHeight = metrics.getHeight();

                // 计算文本居中位置
                int textX = centerX - textWidth / 2;
                int textY = centerY + textHeight / 2 - metrics.getDescent();

                g2d.drawString(label, textX, textY);

                // 更新标签字符
                labelChar++;
                if (labelChar > 'Z') {
                    labelChar = 'A'; // 如果超过Z，则重新从A开始
                }
            }
        }
    }

    /**
     * 生成分区图像和统计信息
     *
     * @param width           拼豆图宽度（块数）
     * @param height          拼豆图高度（块数）
     * @param partitionWidth  分区宽度（块数）
     * @param partitionHeight 分区高度（块数）
     * @param scaledImage     缩放后的原始图像
     */
    private void generatePartitionImagesAndStats(int width, int height, int partitionWidth, int partitionHeight, BufferedImage scaledImage) {
        // 清空之前的分区数据
        partitionImages.clear();
        partitionNames.clear();
        partitionColorStats.clear();

        // 计算分区数量
        int horizontalPartitions = (width + partitionWidth - 1) / partitionWidth;
        int verticalPartitions = (height + partitionHeight - 1) / partitionHeight;

        // 为每个分区生成图像和统计信息
        char labelChar = 'A';
        for (int py = 0; py < verticalPartitions; py++) {
            for (int px = 0; px < horizontalPartitions; px++) {
                // 计算分区的边界
                int partitionStartX = px * partitionWidth;
                int partitionStartY = py * partitionHeight;
                int partitionEndX = Math.min(partitionStartX + partitionWidth, width);
                int partitionEndY = Math.min(partitionStartY + partitionHeight, height);

                // 计算分区尺寸
                int partitionWidthPixels = (partitionEndX - partitionStartX) * DEFAULT_BLOCK_SIZE;
                int partitionHeightPixels = (partitionEndY - partitionStartY) * DEFAULT_BLOCK_SIZE;

                // 创建分区图像
                BufferedImage partitionImage = ImageProcessingUtils.createImage(partitionWidthPixels, partitionHeightPixels, BufferedImage.TYPE_INT_RGB);
                Graphics2D g2d = partitionImage.createGraphics();

                // 启用抗锯齿
                GraphicsUtils.enableAntialiasing(g2d);

                // 填充背景为白色
                g2d.setColor(Color.WHITE);
                g2d.fillRect(0, 0, partitionWidthPixels, partitionHeightPixels);

                // 绘制分区内的拼豆
                Map<String, Integer> colorCount = new HashMap<>();
                for (int x = partitionStartX; x < partitionEndX; x++) {
                    for (int y = partitionStartY; y < partitionEndY; y++) {
                        // 获取缩放后图片在(x, y)位置的颜色
                        int rgb = scaledImage.getRGB(x, y);
                        Color pixelColor = new Color(rgb);

                        // 确保colorList不为空
                        if (colorList == null || colorList.isEmpty()) {
                            log.error("颜色列表为空或未初始化");
                            continue;
                        }

                        // 找到最接近的标准拼豆颜色
                        Color closestColor = ColorUtils.findClosestColor(pixelColor, colorList);

                        // 绘制拼豆方块
                        int blockX = (x - partitionStartX) * DEFAULT_BLOCK_SIZE;
                        int blockY = (y - partitionStartY) * DEFAULT_BLOCK_SIZE;
                        g2d.setColor(closestColor);
                        g2d.fillRect(blockX, blockY, DEFAULT_BLOCK_SIZE, DEFAULT_BLOCK_SIZE);

                        // 绘制颜色标识字符
                        String colorKey = "#" + Integer.toHexString(closestColor.getRGB() & 0x00ffffff).toUpperCase();
                        JSONObject colorInfo = colorMap.getJSONObject(colorKey);

                        if (Objects.nonNull(colorInfo)) {
                            // 获取颜色标识字符
                            String mark = colorInfo.getStr(colorScheme); // 使用当前颜色方案
                            if (mark != null) {
                                String mapKey = colorKey + "_" + mark;

                                // 更新颜色计数
                                colorCount.put(mapKey, colorCount.getOrDefault(mapKey, 0) + 1);

                                // 设置对比色用于绘制文本
                                g2d.setColor(ColorUtils.getContrastColor(closestColor));
                                g2d.setFont(DEFAULT_FONT);

                                // 获取字体度量以计算文本位置
                                FontMetrics metrics = g2d.getFontMetrics();

                                // 计算文本居中位置
                                GraphicsUtils.drawCenteredString(g2d, mark, blockX, blockY, DEFAULT_BLOCK_SIZE, DEFAULT_BLOCK_SIZE);
                            }
                        }
                    }
                }

                // 释放资源
                g2d.dispose();

                // 保存分区数据
                partitionImages.add(partitionImage);
                partitionNames.add(String.valueOf(labelChar));
                partitionColorStats.add(new HashMap<>(colorCount));

                // 更新标签字符
                labelChar++;
                if (labelChar > 'Z') {
                    labelChar = 'A'; // 如果超过Z，则重新从A开始
                }
            }
        }
    }

    /**
     * 生成PDF说明书
     */
    private void generatePdfInstruction() {
        SwingUtilities.invokeLater(() -> {
            progressBar.setValue(0);
            progressBar.setString("正在生成PDF说明书...");
            progressBar.setVisible(true);
        });

        try {
            // 创建文件选择器
            JFileChooser fileChooser = new JFileChooser();
            fileChooser.setDialogTitle("选择PDF保存位置");
            fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
            fileChooser.setFileFilter(new javax.swing.filechooser.FileNameExtensionFilter("PDF文件 (*.pdf)", "pdf"));

            // 设置默认文件名
            String defaultFileName = "拼豆图说明书.pdf";
            fileChooser.setSelectedFile(new File(defaultFileName));

            int result = fileChooser.showSaveDialog(this);
            if (result == JFileChooser.APPROVE_OPTION) {
                File selectedFile = fileChooser.getSelectedFile();
                String outputFile = selectedFile.getAbsolutePath();

                // 确保文件名以.pdf结尾
                if (!outputFile.toLowerCase().endsWith(".pdf")) {
                    outputFile += ".pdf";
                }

                // 生成PDF
                PdfInstructionUtils.generateInstructionPdf(
                        outputFile,
                        cachedOriginalPinDouImage,
                        cachedPinDouImage,
                        partitionImages,
                        partitionNames,
                        (List<Map<String, Object>>)(List<?>)partitionColorStats
                );

                String finalOutputFile = outputFile;
                SwingUtilities.invokeLater(() -> {
                    progressBar.setString("PDF说明书生成完成");
                    progressBar.setVisible(true);
                    JOptionPane.showMessageDialog(this, "PDF说明书已生成: " + finalOutputFile, "完成", JOptionPane.INFORMATION_MESSAGE);
                });

                log.info("PDF说明书生成完成：{}", finalOutputFile);
            } else {
                SwingUtilities.invokeLater(() -> {
                    progressBar.setString("已取消PDF生成");
                    progressBar.setVisible(true);
                });
                log.info("用户取消了PDF生成操作");
            }
        } catch (Exception e) {
            log.error("生成PDF说明书时发生错误", e);
            SwingUtilities.invokeLater(() -> {
                JOptionPane.showMessageDialog(this, "生成PDF说明书时发生错误: " + e.getMessage(), "错误", JOptionPane.ERROR_MESSAGE);
                progressBar.setString("PDF生成失败");
                progressBar.setVisible(true);
            });
        }
    }
}